/*
 * non_blocking.c
 *
 *  Created on: 19 Oct 2011
 *  Copyright 2011 Nicolas Melot
 *
 * This file is part of TDDD56.
 * 
 *     TDDD56 is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     TDDD56 is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with TDDD56. If not, see <http://www.gnu.org/licenses/>.
 * 
 */

#define NON_BLOCKING 2

#ifndef DEBUG
#define NDEBUG
#endif

#include <pthread.h>
#include <string.h>
#include <stdint.h>

inline int
software_cas(void* reg, void* oldval, void* newval, pthread_mutex_t *lock,
    size_t size)
{
  pthread_mutex_lock(lock);
  if (memcmp(reg, oldval, size) == 0)
    {
      memcpy(reg, newval, size);
      pthread_mutex_unlock(lock);

      return 1;
    }
  else
    {
      pthread_mutex_unlock(lock);
      return 0;
    }
  return 1;
}

#if NON_BLOCKING == 0 || NON_BLOCKING == 1
#warning Compiling software CAS

inline int
cas8(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 1);
  }

inline int
cas16(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 2);
  }

inline int
cas32(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 4);
  }

inline int
cas64(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 8);
  }

inline int
cas(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return cas32(reg, oldval, newval, lock);
  }

#else

#ifdef __GNUC__

inline int
cas32(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
{
  return __sync_bool_compare_and_swap((uint32_t*) &reg, (uint32_t*)oldval, (uint32_t*)newval);
  /*int out;

  __asm__("cmpxchgl %4, %2 \n\t" // CAS32(reg (%2), oldval(%%eax), newval(%4))
      "pushf \n\t" //
      "popl %%eax \n\t" //
      "andl $64, %%eax" // Gets result from ZF produced by CAS
      :"=a"(out), "=m"(*(int*)reg)
      :"m"(*(int*)reg), "a"(*(int*)oldval), "r"(*(int*)newval)
  );

  return out;*/
}

inline int
cas16(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
{
  return __sync_bool_compare_and_swap((uint16_t*)reg, *(uint16_t*)oldval, *(uint16_t*)newval);
  /*int out;

  __asm__("cmpxchgw %4, %2 \n\t" // CAS32(reg (%2), oldval(%%eax), newval(%4))
      "pushf \n\t" //
      "popw %%ax \n\t" //
      "andw $64, %%ax" // Gets result from ZF produced by CAS
      :"=a"(out), "=m"(*(int*)reg)
      :"m"(*(int*)reg), "a"(*(int*)oldval), "r"(*(int*)newval)
  );

  return out;*/
}

inline int
cas8(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
{
  return __sync_bool_compare_and_swap((uint8_t*)reg, *(uint8_t*)oldval, *(uint8_t*)newval);
  /*int out;

  __asm__("cmpxchgb %4, %2 \n\t" // CAS32(reg (%2), oldval(%%eax), newval(%4))
      "pushf \n\t" //
      "popw %%ax \n\t" //
      "andb $64, %%al" // Gets result from ZF produced by CAS
      :"=a"(out), "=m"(*(int*)reg)
      :"m"(*(int*)reg), "a"(*(int*)oldval), "r"(*(int*)newval)
  );

  return out;*/
}

inline int
cas64(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
{
  return __sync_bool_compare_and_swap((uint64_t*)reg, *(uint64_t*)oldval, *(uint64_t*)newval);
  /*int out;

  __asm__("cmpxchgb %4, %2 \n\t" // CAS32(reg (%2), oldval(%%eax), newval(%4))
      "pushf \n\t" //
      "popw %%ax \n\t" //
      "andb $64, %%al" // Gets result from ZF produced by CAS
      :"=a"(out), "=m"(*(int*)reg)
      :"m"(*(int*)reg), "a"(*(int*)oldval), "r"(*(int*)newval)
  );

  return out;*/
}

#else

#warning Unsupported compiler; compiling software CAS
inline int
cas8(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 1);
  }

inline int
cas16(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 2);
  }

inline int
cas32(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 4);
  }

inline int
cas64(void* reg, void* oldval, void* newval, pthread_mutex_t *lock)
  {
    return software_cas(reg, oldval, newval, lock, 8);
  }

#endif

#endif

